package com.frank.oj.manager;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.frank.model.entity.judge.RemoteJudgeAccount;
import com.frank.model.entity.problem.*;
import com.frank.oj.crawler.language.LanguageContext;
import com.frank.oj.crawler.problem.*;
import com.frank.oj.service.entity.common.TagEntityService;
import com.frank.oj.service.entity.judge.RemoteJudgeAccountEntityService;
import com.frank.oj.service.entity.problem.LanguageEntityService;
import com.frank.oj.service.entity.problem.ProblemEntityService;
import com.frank.oj.service.entity.problem.ProblemLanguageEntityService;
import com.frank.oj.service.entity.problem.ProblemTagEntityService;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * @author frank
 * @Date 2024/4/4
 */
@Component
public class RemoteProblemManager {

    @Resource
    private RemoteJudgeAccountEntityService remoteJudgeAccountEntityService;
    @Resource
    private ProblemEntityService problemEntityService;

    @Resource
    private ProblemTagEntityService problemTagEntityService;

    @Resource
    private TagEntityService tagEntityService;

    @Resource
    private LanguageEntityService languageEntityService;

    @Resource
    private ProblemLanguageEntityService problemLanguageEntityService;

    public ProblemStrategy.RemoteProblemInfo getOtherOJProblemInfo(String OjName, String problemId, String username) throws Exception {
        ProblemStrategy problemStrategy;
        switch (OjName) {
            case "HDU":
                problemStrategy = new HDUProblemStrategy();
                break;
            case "CF":
                problemStrategy = new CFProblemStrategy();
                break;
            case "POJ":
                problemStrategy = new POJProblemStrategy();
                break;
            case "GYM":
                problemStrategy = new GYMProblemStrategy();
                break;
            case "SPOJ":
                problemStrategy = new SPOJProblemStrategy();
                break;
            case "AC":
                problemStrategy = new AtCoderProblemStrategy();
                break;
            default:
                throw new Exception("未知的OJ的名字，暂时不支持！");
        }

        ProblemContext problemContext = new ProblemContext(problemStrategy);

        try {
            return problemContext.getProblemInfo(problemId, username);
        } catch (IllegalStateException e) {
            if (Objects.equals("GYM", OjName)) {
                LambdaQueryWrapper<RemoteJudgeAccount> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(RemoteJudgeAccount::getOj, "CF");
                List<RemoteJudgeAccount> remoteJudgeAccounts = remoteJudgeAccountEntityService.list(queryWrapper);

                if (!CollectionUtils.isEmpty(remoteJudgeAccounts)) {
                    RemoteJudgeAccount account = remoteJudgeAccounts.get(0);
                    return problemContext.getProblemInfoByLogin(problemId, username, account.getUsername(), account.getPassword());
                }
            }
            return null;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Problem adminAddOtherOJProblem(ProblemStrategy.RemoteProblemInfo remoteProblemInfo, String OJName) {
        Problem problem = remoteProblemInfo.getProblem();
        boolean addProblemResult = problemEntityService.save(problem);

        LambdaQueryWrapper<Language> queryWrapper = new LambdaQueryWrapper<>();
        if (OJName.equals("GYM")) {
            queryWrapper.eq(Language::getOj, "CF");
        } else {
            queryWrapper.eq(Language::getOj, OJName);
        }
        List<Language> languageList = languageEntityService.list(queryWrapper);
        List<ProblemLanguage> problemLanguages = new LinkedList<>();

        if (!CollectionUtil.isEmpty(remoteProblemInfo.getLangIdList())) {
            LanguageContext languageContext = new LanguageContext(remoteProblemInfo.getRemoteOJ());
            List<Language> languages = languageContext.buildLanguageListByIds(languageList, remoteProblemInfo.getLangIdList());

            for (Language language : languages) {
                problemLanguages.add(new ProblemLanguage().setPid(problem.getId()).setLid(language.getId()));
            }
        } else {
            for (Language language : languageList) {
                problemLanguages.add(new ProblemLanguage().setPid(problem.getId()).setLid(language.getId()));
            }
        }
        boolean addProblemLanguageResult = problemLanguageEntityService.saveOrUpdateBatch(problemLanguages);

        boolean addProblemTagResult = true;
        List<Tag> addTagList = remoteProblemInfo.getTagList();
        LinkedList<Tag> needAddTagList = new LinkedList<>();

        HashMap<String, Tag> tagFlag = new HashMap<>();

        if (addTagList != null && !addTagList.isEmpty()) {
            LambdaQueryWrapper<Tag> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.eq(Tag::getOj, OJName);
            List<Tag> tagList = tagEntityService.list(queryWrapper1);
            for (Tag tag : tagList) {
                tagFlag.put(tag.getName().toUpperCase(), tag);
            }

            for (Tag tag : addTagList) {
                Tag tag1 = tagFlag.get(tag.getName().toUpperCase());
                // 数据库中没有
                if (tag1 == null) {
                    tag.setOj(OJName);
                    needAddTagList.add(tag);
                } else {
                    needAddTagList.add(tag1);
                }
            }
            tagEntityService.saveOrUpdateBatch(needAddTagList);

            LinkedList<ProblemTag> problemTags = new LinkedList<>();
            for (Tag tag : needAddTagList) {
                problemTags.add(new ProblemTag().setTid(tag.getId()).setPid(problem.getId()));
            }
            addProblemTagResult = problemTagEntityService.saveOrUpdateBatch(problemTags);
        } else {
            // 但没有标签信息时，添加OJ名称为标签
            LambdaQueryWrapper<Tag> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Tag::getName, OJName);
            Tag OJNameTag = tagEntityService.getOne(wrapper, false);
            if (OJNameTag == null) {
                OJNameTag = new Tag();
                OJNameTag.setOj(OJName);
                OJNameTag.setName(OJName);
                tagEntityService.saveOrUpdate(OJNameTag);
            }
            addProblemTagResult = problemTagEntityService.saveOrUpdate(new ProblemTag().setTid(OJNameTag.getId()).setPid(problem.getId()));
        }
        if (addProblemResult && addProblemTagResult && addProblemLanguageResult) {
            return problem;
        } else {
            return null;
        }
    }
}
